用 Node.js 实现网站动态后端

Debian 10

安装 node.js

fnm 版本管理器

fnm 安装脚本

  1. 使用脚本 对于 bashzshfish shells,确保已安装 curlunzip ,然后执行脚本:(重启或 source)
curl -fsSL https://fnm.vercel.app/install | bash
  1. 手动安装
  2. 移除:删除主目录中的 .fnm 文件夹。编辑 shell 配置以删除对 fnm 的任何引用。

使用

  1. 安装 Node.js:
  2. 通过 fnm env 设置环境变量,将 --use-on-cd 选项添加到 shell 设置中,以在目录包含 .node-version.nvmrc 文件时自动运行 fnm use 。for Zsh,在 .zshrc profile 中添加:(重启或 source)
eval "$(fnm env --use-on-cd)"

NodeSource 二进制发行版

  1. 安装说明
  2. 卸载说明
  3. 手动安装

创建 Node.js 项目

  1. 进入目录
cd /var/www
mkdir server
cd server
mkdir dreamforest
cd dreamforest
  1. 初始化项目
npm init -y
  1. 安装 express
npm install express

创建 Express 应用

创建 dreamforest.js 文件:

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const port = 3000;

app.get('//server/directory', (req, res) => {
    const directoryPath = path.join(__dirname, '../../dreamforest.com/article');
    fs.readdir(directoryPath, function (err, files) {
        if (err) {
            return console.log('Unable to scan directory: ' + err);
        } 
        let mdFiles = files
            .filter(el => path.extname(el) === '.md')
            .map(file => path.join(directoryPath, file));
        res.send(mdFiles);
    });
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`)
})
node dreamforest.js

在这个上下文中,2>&1 是一个 shell 命令,用于重定向标准错误(stderr)到与标准输出(stdout)相同的位置。让我解释一下它的具体含义:

综上所述,2>&1 的意思是将标准错误重定向到与标准输出相同的位置,也就是将错误信息输出到同一个文件(log.txt)中。这样做可以将程序的标准输出和标准错误一起记录在同一个日志文件中。

配置 Nginx

server {
    listen 80;
    server_name your-domain.com;
    root /path/to/your/files;

    location /server/ {
        proxy_pass http://localhost:3000;
    }

    # HTTP 访问,目录下没有默认索引文件,会自动生成并返回一个资源列表。
    # location /static/ {
    #     autoindex on;
    # }
}

Express 路由

这里的 app.get('/', (req, res) => { ... }) 中的 / 路径是相对于你的 Express 应用的基础 URL,即 http://your-domain.com/myapp/ 来说的。在后面加上更多路径,如果 Exptresss 中设置了对应路由就会匹配上,否则返回 404 Not Found。

在 HTML 中发送请求

<!DOCTYPE html>
<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
</head>
<body>

<ul id="mdFileList"></ul>

<script>
$.get('/mdFiles', function(data) {
    for (let i = 0; i < data.length; i++) {
        $('#mdFileList').append('<li>' + data[i] + '</li>');
    }
});
</script>

</body>
</html>

查看更多日志信息

为了获取更多的调试信息,你可以在你的代码中添加更多的 console.log() 语句。此外,你也可以使用一些中间件来获取详细的日志,例如 morgan。以下是一个使用 morgan 中间件的示例:

首先,安装 morgan:

npm install morgan

然后,将其添加到你的应用中:

// 引入 morgan 模块
const morgan = require('morgan');

// ...

// 使用 morgan 中间件,配置为 'dev' 模式
app.use(morgan('dev'));

// ...

morgan 中间件将自动记录所有的 HTTP 请求,并将相关的信息打印到控制台。这些信息包括请求方法(GET、POST 等)、请求的 URL、响应状态码、响应时间等。

你也可以根据需要自定义 morgan 的配置,例如选择不同的预设格式,或者自定义你的日志格式。

此外,你可能还想要使用一些错误处理中间件,例如 express-async-errors,来捕获异步函数中的错误。

当你在 express 应用中使用 async/await 语句时,如果没有正确的错误处理,那么可能会导致未捕获的 Promise 错误。为了解决这个问题,你可以使用 express-async-errors 中间件:

首先,安装 express-async-errors:

npm install express-async-errors

然后,在你的应用中使用它:

// 引入 express-async-errors 模块
require('express-async-errors');

// ...

app.get('/directory', async (req, res) => {
    // ...
    // 你的代码
    // ...
});

// 错误处理中间件
app.use((err, req, res, next) => {
    console.error(err);
    res.status(500).send('Something went wrong');
});

在上面的代码中,我们使用了 express-async-errors 中间件,它会自动捕获所有的异步错误,并将其传递给错误处理中间件。然后,我们定义了一个错误处理中间件,它会在控制台打印出错误信息,并向客户端发送一个 500 错误。

这些中间件应该能帮助你获取到更多的日志信息,并帮助你更好地识别和解决问题。

后台进程

如果你直接退出bash,Node.js应用会立即停止。因为当你退出bash时,所有由该bash会话启动的子进程(包括你的Node.js应用)都会被终止。

要让Node.js应用在后台运行,你可以使用以下方法:

  1. 使用 & 符号:在启动应用时在命令后加上 & 符号,就可以让应用在后台运行。例如:
   node app.js &

这样做会让应用在后台运行,但是如果你退出bash会话,应用还是会被终止。

  1. 使用 nohup 命令:nohup 命令可以让应用忽略所有挂断(HUP)信号,这样即使bash会话被终止,应用也能继续运行。例如: (nohup node app.js > output.log &
   nohup node app.js &

这样做会让应用在后台运行,而且即使退出bash会话,应用也能继续运行。

  1. 使用 screentmux:这两个都是命令行工具,可以创建多个独立的会话。你可以在一个会话中启动应用,然后退出会话,应用就能在后台继续运行。后来你可以再次连接到那个会话,查看应用的运行状态。

  2. 使用进程管理器:例如 PM2,这是一个专门用于管理Node.js应用的进程管理器。使用PM2,你可以很容易地启动、停止和重启应用,还可以查看应用的运行状态和日志。

以上的每种方法都有其优缺点,你可以根据你的具体需求选择最合适的方法。

后台管理实践

  1. no hang up
nohup node dreamforest.js &
  1. 检查和管理
ps -ef | grep node
  1. 查看正在运行的后台任务:
jobs
  1. 从 Shell 的作业表中删除作业(移除该作业的跟踪),终止时不发送 SIGHUP 信号到该作业。对于 zsh。若为 bash 则加 -h 。1 是作业编号。
disown %1

在服务器提前解析 md

  1. 安装模块
npm install markdown-it highlight.js
  1. 引入模块
// 引入 markdown-it 模块
const MarkdownIt = require('markdown-it');

const hljs = require('highlight.js');

// 创建一个新的 markdown-it 实例
const md = new MarkdownIt({
    highlight: function (str, lang) {
        if (lang && hljs.getLanguage(lang)) {
            try {
                return hljs.highlight(str, { language: lang }).value;
            } catch (__) { }
        }
        return ''; // 使用外部默认转义
    },
});
  1. 在 Node.js 应用的定时更新缓存函数中添加解析步骤,并在 appget 中获取与缓存键值对的键 cacheKey 相对应的查询参数 file 的值。

更多

二:解析 MD

是的,你的理解是正确的。Node.js应用可以读取特定目录下的所有.md文件,并生成HTML页面,包含所有.md文件的链接列表。下面是一个具体的实现方案:

首先,安装expressmarkdown-it库,通过Node.js的包管理器npm进行安装:

npm install express markdown-it

然后,创建一个Node.js应用:

const express = require('express');
const fs = require('fs');
const path = require('path');
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();
const app = express();
const port = 3000;

app.get('/', (req, res) => {
    const directoryPath = path.join(__dirname, '/');
    fs.readdir(directoryPath, function (err, files) {
        if (err) {
            return console.log('Unable to scan directory: ' + err);
        } 
        let mdFiles = files.filter(el => path.extname(el) === '.md');
        let html = mdFiles.map(file => `<li><a href="/${file}">${path.basename(file, '.md')}</a></li>`).join('');
        res.send(`<ul>${html}</ul>`);
    });
});

app.get('/:mdFile', (req, res) => {
    const mdFilePath = path.join(__dirname, req.params.mdFile);
    fs.readFile(mdFilePath, 'utf8', (err, data) => {
        if (err) {
            res.sendStatus(404);
            return;
        }
        const html = md.render(data);
        res.send(html);
    });
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`)
})

在上述代码中,我们首先创建一个Express应用。然后,我们在根路径'/'上设置一个处理函数,该函数读取当前目录下的所有.md文件,并生成一个HTML列表,其中包含所有.md文件的链接。

我们还在路径'/:mdFile'上设置了一个处理函数,该函数读取请求的.md文件,并使用markdown-it库将其转换为HTML。

最后,我们启动了应用,监听3000端口。

然后,你可以在Nginx中设置一个反向代理,将所有到/的请求转发到这个Node.js应用。这可以在Nginx的配置文件中完成,如下所示:

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://localhost:3000;
    }
}

在上述配置中,所有到/的请求都会被转发到运行在3000端口的Node.js应用。

注意,以上代码示例仅供参考,并未处理所有可能的错误情况。在实际使用中,你可能需要更多的错误处理代码,以确保应用的稳定性和安全性。

三:加语法高亮和按钮

首先,你需要安装 Node.js 和 npm(Node 包管理器)。然后,使用 npm 来安装必要的库,运行以下命令:

npm install express markdown-it highlight.js clipboardy

然后,我们可以在后端创建一个 Express 应用,该应用将处理 Markdown 文件并添加复制按钮。这是一个使用 markdown-ithighlight.js 的基本示例。

index.js 文件中:

const express = require('express');
const fs = require('fs');
const path = require('path');
const MarkdownIt = require('markdown-it');
const hljs = require('highlight.js');
const md = new MarkdownIt({
    highlight: function (str, lang) {
        if (lang && hljs.getLanguage(lang)) {
            try {
                return hljs.highlight(str, { language: lang }).value;
            } catch (__) {}
        }
        return ''; 
    }
});

const app = express();
const port = 3000;

app.get('/:mdFile', (req, res) => {
    const mdFilePath = path.join(__dirname, req.params.mdFile);
    fs.readFile(mdFilePath, 'utf8', (err, data) => {
        if (err) {
            res.sendStatus(404);
            return;
        }
        let html = md.render(data);
        // Add copy button to each code block
        html = html.replace(/<pre><code class="(.+?)">/g, '<pre><button class="copy-button">Copy</button><code class="$1">');
        res.send(html);
    });
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`)
});

上述代码会把每个代码块前加上复制按钮,然后前端 JavaScript 可以使用 ClipboardJS 或其它库来处理按钮的点击事件,从而实现复制功能。注意,该实现方式只是基础实现,复制按钮的样式和交互效果需要你在前端进一步处理。

在前端 JavaScript 中,你可以使用类似下面的代码来处理复制按钮的点击事件(需要先引入 ClipboardJS):

const clipboard = new ClipboardJS('.copy-button', {
    target: function(trigger) {
        return trigger.nextElementSibling;
    }
});

clipboard.on('success', function(e) {
    e.trigger.textContent = '已复制';
    setTimeout(function() {
        e.trigger.textContent = '复制';
    }, 2000);
    e.clearSelection();
});

请注意,这种实现方式可能会有一些安全隐患,比如 XSS 攻击。你可能需要对生成的 HTML 进行额外的清理和验证,以确保它不包含任何恶意的代码。

安装 node.js 之后怎么创建应用?下面这个代码会解析根目录下的所有 md 文件吗?在html要怎么访问某一个特定文件的 markdown 解析成的 html?node.js 怎么调试输入和输出?在 vscode 中怎么调试?highlight.js 默认的语法高亮文件颜色搭配不好看,怎么更改为 github 风格的语法高亮颜色文件?

  1. 创建Node.js应用 在安装了Node.js之后,你可以通过以下步骤来创建一个新的应用:
  1. 解析所有md文件 下面的Node.js代码片段会读取和解析指定目录中的所有Markdown文件:
const fs = require('fs');
const path = require('path');
const markdownIt = require('markdown-it');
const md = markdownIt();

fs.readdir('./', (err, files) => {
  if (err) {
    console.error(err);
    return;
  }

  files.forEach(file => {
    if (path.extname(file) === '.md') {
      fs.readFile(file, 'utf8', (err, data) => {
        if (err) {
          console.error(err);
          return;
        }

        const result = md.render(data);
        // do something with result
      });
    }
  });
});

这段代码会在当前目录('./')下查找所有的.md文件,读取它们的内容,然后使用markdown-it将它们转换为HTML。

  1. 访问特定文件的HTML 为了让用户能够在浏览器中访问到这些HTML文件,你可能需要创建一个Web服务器,例如使用Express.js。你可以为每个Markdown文件创建一个路由,然后在用户请求这个路由时返回对应的HTML。

  2. 调试Node.js 在VS Code中,你可以使用内置的Node.js调试器来调试你的应用。首先,你需要在你的项目中创建一个新的调试配置文件(.vscode/launch.json),然后在这个文件中配置你的调试环境。然后,你可以在你的代码中设置断点,然后启动调试器来运行你的代码。

  3. 更改highlight.js的颜色主题 highlight.js提供了多种不同的颜色主题,包括一种类似GitHub的颜色主题。你可以在你的网页中引入这个主题的CSS文件来更改颜色主题:

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css">

请记住,你需要先引入highlight.js的主要JavaScript文件,然后再引入颜色主题的CSS文件。

总的来说,你正在进行的这个项目需要涉及到多个方面的知识,包括Node.js、文件I/O、Markdown解析、HTTP和Web服务器、以及调试和CSS样式。这可能会需要一些时间和耐心来学习和实现。但是,我希望上面的信息能够帮助你开始这个项目,并在你遇到问题时有所指引。